package com.distribute.framework.core.sql.redis;

import com.distribute.framework.core.lang.Serialize;
import com.distribute.framework.core.web.helper.ConfigHelper;
import com.distribute.framework.core.web.helper.Utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class RedisSupport{
	
	private static Map<String, RedisSupport> redises = new HashMap<String, RedisSupport>();//DAO	
	public synchronized static RedisSupport getRedis(String name){
		RedisSupport redisSupport = redises.get(name);
		if(redisSupport!=null)
			return redisSupport;
		redisSupport = new RedisSupport(name);
		redises.put(name, redisSupport);
		return redisSupport;
	}
	
	JedisPool pool;
	JedisPoolConfig config;
	Integer index = 0;
	public Boolean initialize = false;
	public Boolean debug = false;
	private RedisSupport(String name){
		try{
			Boolean bAll = true;
			String[] configKeys={"host","port","max","min","timeout","index"};
			for(String key:configKeys){
				if(!ConfigHelper.contains(name+"."+key)){
					String str = "\n";
					str+=name+"####################redis数据源["+name+"]#######################\n";
					for(int i=0;i<configKeys.length;i++){
						String k = configKeys[i];
						str+=name+"."+k+"=\n";
					}
					bAll = false;				
				}
			}
			if(bAll || true){
				config = new JedisPoolConfig();
		        //控制一个pool可分配多少个jedis实例，通过pool.getResource()来获取；
		        //如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted(耗尽)。
		        //config.setMaxActive(Integer.parseInt(Utils.config(name+".max")));
		        //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
		        config.setMaxIdle(Integer.parseInt(Utils.config(name+".min")));
		        //表示当borrow(引入)一个jedis实例时，最大的等待时间，如果超过等待时间，则直接抛出JedisConnectionException；
		        config.setMaxWaitMillis(Long.parseLong(Utils.config(name+".timeout")));
		        //在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；
		        config.setTestOnBorrow(false);
		        //当前jedis连接的库号
		        index = Integer.parseInt(Utils.config(name+".index","0"));
				//实例化jedis池
		        String host = Utils.config(name+".host");
		        int port = Integer.parseInt(Utils.config(name+".port","6379"));
		        int timeout = Integer.parseInt(Utils.config(name+".timeout","3000"));
		        String password = Utils.config(name+".password");
		        JedisPool d ;
				pool = new JedisPool(config,host , port,timeout,password);
				//初始化
				Jedis jedis = this.getJedis();
				this.pool.returnResource(jedis);
				initialize = true;
			}else{
				Utils.trace("ERROR - 初始化Reids时出错，在配置文件中未找到"+name+".***属性(host,port,max,min,timeout,index)");
			}
		}catch(Exception ex){
			ex.printStackTrace();
		}
	}
	

	public Jedis getJedis(){
		try{
			Jedis jedis = pool.getResource();
			if(this.index!=jedis.getDB().intValue())
				jedis.select(this.index);
			return jedis;
		}catch(Exception ex){
			Utils.trace(ex.getMessage());
		}
		return null;
	}
	
	public void retJedis(Jedis jedis){
		if(jedis!=null)
			this.pool.returnResource(jedis);
	}	
	
		
	Date last_console_time = new Date();
	Boolean console = false;
	public void console(Object...objects){
		if(!this.initialize)return;
		if((new Date().getTime()-last_console_time.getTime())>(1000*30)){
			Jedis jedis = this.getJedis();
			try{
				console = Utils.equals("true",jedis.get("INI.console"));
				debug = (Utils.parseLong(Utils.or(jedis.get("INI.debug"),String.valueOf(new Date().getTime())))>new Date().getTime());
			}catch(Exception ex){
				Utils.trace(ex.getMessage());
			}finally{
				if(jedis!=null)
					this.pool.returnResource(jedis);
			}
			last_console_time = new Date();
		}
		if(!console)return;
		String strConsole = "REDIS -";
		for(Object object:objects)
			strConsole+=" "+object;
		System.out.println(strConsole);
	}
	
	
	
	public Object setMap(String name, String key, Object value) {
		if(!this.initialize)return null;
		console("set map value",name,key,value);
		if(value==null){
			deleteMap(name,key);
			return null;
		}
		Jedis jedis = this.getJedis();
		try{
			jedis.hset(("MAP."+name).getBytes(),key.getBytes(),Serialize.serialize(value));
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return value;
	}
	public <T> T getMap(String name, String key) {
		if(!this.initialize)return null;
		console("get map value",name,key);
		Jedis jedis = this.getJedis();
		try{
			byte[] bytes = jedis.hget(("MAP."+name).getBytes(),key.getBytes());
			if(bytes==null||bytes.length==0)
				return null;
			return (T) Serialize.unserialize(bytes);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	public Set<String> getMapKeys(String name) {
		if(!this.initialize)return null;
		console("get map keys",name);
		Jedis jedis = this.getJedis();
		try{
			Set<String> keys = jedis.hkeys(("MAP."+name));
			return keys;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	public Long deleteMap(String name, String... key) {
		if(!this.initialize)return null;
		console("delete value",name,key);
		Jedis jedis = this.getJedis();
		try{
			String[] keys=new String[key.length];
			for(int i=0;i<key.length;i++){
				keys[i]=key[i];
			}
			Long returnMap = jedis.hdel("MAP."+name, keys);
			return returnMap;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	public Long clearMap(String name) {
		if(!this.initialize)return null;
		console("clear value",name);
		Jedis jedis = this.getJedis();
		try{
			return jedis.del("MAP."+name);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	public void setMapExpire(String name,int seconds){
		if(!this.initialize)return;
		Jedis jedis = this.getJedis();
		try{
			jedis.expire(("MAP."+name).getBytes(), seconds);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
	}
	public void setMapExpireTime(String name,Date atTime){
		if(!this.initialize)return;
		Jedis jedis = this.getJedis();
		try{
			jedis.expireAt(("MAP."+name).getBytes(), atTime.getTime()/1000);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
	}
	
	
	
	
	
	
	
	
	
	
	/**
	 * 列表末尾添加元素
	 * @param name
	 * @param value
	 * @return
	 */
	public Object push(String name, Object value) {
		if(!this.initialize)return null;
		console("push list value",name,value);
		if(value==null){
			return null;
		}
		Jedis jedis = this.getJedis();
		try{
			jedis.rpush(("LIST."+name).getBytes(),Serialize.serialize(value));
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return value;
	}
	/**
	 * 获取列表头部元素
	 * @param <T>
	 * @param name
	 * @return
	 */
	public <T> T pop(String name) {
		if(!this.initialize)return null;
		console("get list value",name);
		Jedis jedis = this.getJedis();
		try{
			byte[] bytes = jedis.lpop(("LIST."+name).getBytes());
			if(bytes==null||bytes.length==0)
				return null;
			return (T) Serialize.unserialize(bytes);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	public Long deleteList(String name) {
		if(!this.initialize)return null;
		console("delete list",name);
		Jedis jedis = this.getJedis();
		try{
			return jedis.del("LIST."+name);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	public void setListExpire(String name,int seconds){
		if(!this.initialize)return;
		Jedis jedis = this.getJedis();
		try{
			jedis.expire(("LIST."+name).getBytes(), seconds);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
	}
	public void setListExpireTime(String name,Date atTime){
		if(!this.initialize)return;
		Jedis jedis = this.getJedis();
		try{
			jedis.expireAt(("LIST."+name).getBytes(), atTime.getTime()/1000);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	public String setValue(String flag, String key, String value) {
		if(!this.initialize)return null;
		console("set value",flag,key,value);
		
		Jedis jedis = this.getJedis();
		try{
			String result = jedis.set("KV."+flag+"."+key, value);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return value;
	}
	public String setValue(String flag, String key, String value,Integer seconds) {
		if(!this.initialize)return null;
		console("set value",flag,key,value,seconds);	
		
		Jedis jedis = this.getJedis();
		try{
			String result = jedis.setex("KV."+flag+"."+key,seconds,value);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return value;
	}
	public String getValue(String flag, String key) {
		if(!this.initialize)return null;
		console("get value",flag,key);		
		
		Jedis jedis = this.getJedis();
		try{
			String returnValue = jedis.get("KV."+flag+"."+key);
			return returnValue;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	public Long deleteValue(String flag, String... key) {
		if(!this.initialize)return null;
		console("delete value",flag,key);
		
		Jedis jedis = this.getJedis();
		try{
			String[] keys=new String[key.length];
			for(int i=0;i<key.length;i++){
				keys[i]="KV."+flag+"."+key[i];
			}
			Long returnValue = jedis.del(keys);
			return returnValue;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	public Long clearValue(String flag) {
		if(!this.initialize)return null;
		console("clear value",flag);	
		
		this.setValue(flag, "CLEARING", "true");
		Jedis jedis = this.getJedis();
		
		try{
			//EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 KV.*
			Object returnValue = jedis.eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))",0,"KV."+flag+"."+"*");
			return Utils.parseLong(returnValue);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	
	public void setValueExpire(String flag, String key,int seconds){
		if(!this.initialize)return;
		Jedis jedis = this.getJedis();
		try{
			jedis.expire(("KV."+flag+"."+key).getBytes(), seconds);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
	}
	
	
	public void setValueExpireTime(String flag, String key,Date atTime){
		if(!this.initialize)return;
		Jedis jedis = this.getJedis();
		try{
			jedis.expireAt(("KV."+flag+"."+key).getBytes(), atTime.getTime()/1000);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	public Object setObject(String flag,String key, Object value) {
		if(!this.initialize)return null;
		console("set object",flag,key,value);
		
		if(value==null){
			this.deleteObject(flag, key);
			return null;
		}
		Jedis jedis = this.getJedis();
		try{
			String result = jedis.set(("OBJ."+flag+"."+key).getBytes(), Serialize.serialize(value));
			return value;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return value;
	}
	public Object setObject(String flag,String key, Object value,Integer seconds) {
		if(!this.initialize)return null;
		console("set object",flag,key,value,seconds);	
		
		if(value==null){
			this.deleteObject(flag, key);
			return null;
		}
		Jedis jedis = this.getJedis();
		try{
			String result = jedis.setex(("OBJ."+flag+"."+key).getBytes(),seconds,Serialize.serialize(value));
			return value;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return value;
	}
	public <T> T getObject(String flag,String key) {
		if(!this.initialize)return null;
		console("get object",flag,key);		
		
		Jedis jedis = this.getJedis();
		try{
			byte[] bytes = jedis.get(("OBJ."+flag+"."+key).getBytes());
			if(bytes==null||bytes.length==0)
				return null;
			T returnValue = (T) Serialize.unserialize(bytes);
			return (T) returnValue;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	
	public <T> Long deleteObject(String flag, String key) {
		if(!this.initialize)return null;
		console("delete object",flag,key);		
		
		Jedis jedis = this.getJedis();
		try{
			Long returnValue = jedis.del(("OBJ."+flag+"."+key).getBytes());
			return returnValue;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	
	
	public <T> Long clearObject(String flag) {
		if(!this.initialize)return null;
		console("clear object",flag);	
		
		this.setObject(flag, "CLEARING", "true");
		Jedis jedis = this.getJedis();
		try{
			//EVAL "return redis.call('del', unpack(redis.call('keys', 'OBJ.*')))" 0
			//EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 OBJ.*
			//Object returnValue = jedis.eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))",0,"OBJ."+flag+"."+"*");
			Object returnValue = jedis.eval("local keys = redis.call('keys', ARGV[1]) for i=1,#keys,1000 do redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) end return #keys",0,"OBJ."+flag+"."+"*");
			return Utils.parseLong(returnValue);
		}catch(Exception ex){
			////Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
		
	
	public void setObjectExpire(String flag, String key,int seconds){
		if(!this.initialize)return;
		Jedis jedis = this.getJedis();
		try{
			jedis.expire(("OBJ."+flag+"."+key).getBytes(), seconds);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
	}
	
	
	public void setObjectExpireTime(String flag, String key,Date toTime){
		if(!this.initialize)return;
		Jedis jedis = this.getJedis();
		try{
			jedis.expireAt(("OBJ."+flag+"."+key).getBytes(), toTime.getTime()/1000);
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
	}
	
	
	
	
	
	
	public String[] getKeys(String pattern) {
		if(!this.initialize)return null;
		console("keys",pattern);
		
		Jedis jedis = this.getJedis();
		try{
			return jedis.keys(pattern).toArray(new String[]{});
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	
	
	
	public Long incr(String key) {
		if(!this.initialize)return null;
		Jedis jedis = this.getJedis();
		try{
			Long returnValue = jedis.incr("DI."+key);
			return returnValue;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}
	
	public Long decr(String key) {
		if(!this.initialize)return null;
		Jedis jedis = this.getJedis();
		try{
			Long returnValue = jedis.decr("DI."+key);
			return returnValue;
		}catch(Exception ex){
			//Utils.trace(ex.getMessage());
		}finally{
			if(jedis!=null)
				this.pool.returnResource(jedis);
		}
		return null;
	}


	
	public static void main(String...strings){
		Date start = new Date();
		RedisSupport r = RedisSupport.getRedis("lazy.redis.0");
		r.clearObject("aaa");
	}
	
}